﻿using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;
using System.Net;
using System.Net.Sockets;

namespace Server
{
    /// <summary>
    /// Helpers.
    /// </summary>
    public static class Helpers
    {
        #region Methods

        /// <summary>
        /// Create the remote IP of a given <see cref="T:System.Net.Sockets.TcpClient" />.
        /// </summary>
        /// <param name="tcpClient">The <see cref="T:System.Net.Sockets.TcpClient" />.</param>
        /// <returns>The remote IP of the specified <see cref="T:System.Net.Sockets.TcpClient" />.</returns>
        /// <exception cref="T:System.Net.Sockets.SocketException">An error occurred when attempting to access the socket -or - The address family is <see cref="F:System.Net.Sockets.AddressFamily.InterNetworkV6" /> and the address is bad.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Net.Sockets.Socket"/> has been closed.</exception>
        /// <exception cref="T:System.InvalidOperationException">The remote IP could not be determined.</exception>
        public static string GetRemoteIp(this TcpClient tcpClient)
        {
            Contract.Requires<ArgumentNullException>(tcpClient != null);
            Contract.Ensures(!String.IsNullOrWhiteSpace(Contract.Result<string>()));

            string remoteIp = null;
            var client = tcpClient.Client;
            if (client != null)
            {
                var remoteEndPoint = client.RemoteEndPoint as IPEndPoint;
                if (remoteEndPoint != null)
                {
                    var address = remoteEndPoint.Address;
                    if (address != null)
                        remoteIp = address.ToString();
                }
            }
            if (String.IsNullOrWhiteSpace(remoteIp))
                throw new InvalidOperationException("The remote IP could not be determined.");
            return remoteIp;
        }

        /// <summary>
        /// Create a compiled default constructor for a given <see cref="T:System.Type" />.
        /// </summary>
        /// <param name="type">The <see cref="T:System.Type" />.</param>
        /// <returns>The default constructor of the specified <see cref="T:System.Type" />.</returns>
        /// <exception cref="T:System.ArgumentException">The type does not have a constructor without parameters.</exception>
        public static Func<object> CreateDefaultConstructor(this Type type)
        {
            Contract.Requires<ArgumentNullException>(type != null);
            Contract.Ensures(Contract.Result<Func<object>>() != null);

            var result = Expression.Lambda<Func<object>>(Expression.New(type)).Compile();
            if (result == null)
                throw new ArgumentException(String.Format("The type {0} does not have a constructor without parameters.", type.FullName), "type");
            return result;
        }

        #endregion
    }
}
